
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>SpringBoot中SPI机制 - 学习卡片</title>
      <style>
        body { font-family: sans-serif; background-color: #f0f8ff; color: #333; display: flex; flex-direction: column; align-items: center; padding: 50px 20px; }
        .header h1 { font-size: 32px; }
        .grid-container { display: grid; grid-template-columns: repeat(3, 1fr); gap: 28px; width: 100%; max-width: 1200px; }
        .card-container { perspective: 1200px; cursor: pointer; height: 250px; }
        .card { width: 100%; height: 100%; position: relative; transform-style: preserve-3d; transition: transform 0.7s; border-radius: 16px; box-shadow: 0 4px 16px rgba(0,0,0,0.08); }
        .card-container.flipped .card { transform: rotateY(180deg); }
        .card-face { position: absolute; width: 100%; height: 100%; backface-visibility: hidden; display: flex; flex-direction: column; box-sizing: border-box; border-radius: 16px; background-color: #fff; padding: 24px; }
        .card-back { background-color: #f0fff4; transform: rotateY(180deg); justify-content: space-between; }
        .card-category { font-size: 14px; color: #0052d9; margin-bottom: 8px; font-weight: 500; }
        .card-question { font-size: 20px; font-weight: 500; flex-grow: 1; display: flex; align-items: center; justify-content: center; text-align: center; }
        .card-answer-wrapper { flex-grow: 1; overflow-y: auto; }
        .card-answer { font-size: 15px; line-height: 1.7; }
        .card-footer { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 16px; margin-top: 16px; }
        .card-source { font-size: 13px; color: #8a919f; border-top: 1px solid #f0f0f0; padding-top: 12px; margin-top: 12px; }
      </style>
    </head>
    <body>
      <div class="header">
        <h1>SpringBoot中SPI机制 - 学习卡片</h1>
      </div>
      <div class="grid-container">
        
    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">理论</div>
          <div class="card-question">什么是 SPI (Service Provider Interface) 机制？它与传统 API 的调用关系有何不同？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">理论</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">SPI 是一种服务发现机制，允许应用程序动态地发现和加载组件。它颠倒了传统 API 的调用关系，从“调用方依赖接口”转变为“接口寻找实现方”。</div>
          </div>
          <div class="card-source">来源: 一、SPI 机制的基本概念 -> 1. 什么是 SPI 机制</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">理论</div>
          <div class="card-question">请阐述 SPI (Service Provider Interface) 与 API (Application Programming Interface) 的主要区别。</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">理论</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">主要区别在于控制权：API 是服务方提供给调用方使用的接口，控制权在调用方；而 SPI 是框架提供给扩展方实现的接口，控制权在框架方。通俗地讲，API 是“我”主动调用你，SPI 是“我”等着被调用。</div>
          </div>
          <div class="card-source">来源: 一、SPI 机制的基本概念 -> 3. SPI 与 API 的区别与联系</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">SpringBoot 的 SPI 机制是基于哪个核心类和配置文件实现的？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">SpringBoot 的 SPI 核心类是 `SpringFactoriesLoader`，它负责加载 `META-INF/spring.factories` 文件中配置的类。这个文件是 SpringBoot SPI 机制的核心配置文件。</div>
          </div>
          <div class="card-source">来源: 二、SpringBoot SPI 机制的实现原理 -> 2. SpringFactoriesLoader 详解</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">特性</div>
          <div class="card-question">相比于 JDK 原生的 SPI，SpringBoot 的 SPI 机制提供了哪些增强功能？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">特性</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">SpringBoot 对 JDK SPI 的主要增强点包括：支持 key-value 形式的配置、支持同一接口多个实现类的排序、支持按条件加载以及提供缓存优化。</div>
          </div>
          <div class="card-source">来源: 二、SpringBoot SPI 机制的实现原理 -> 1. SpringBoot 对 JDK SPI 的增强</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">应用</div>
          <div class="card-question">SpringBoot 的自动配置功能是如何利用 SPI 机制实现的？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">应用</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">SpringBoot 的自动配置通过 `@EnableAutoConfiguration` 注解触发。该注解导入的 `AutoConfigurationImportSelector` 会使用 `SpringFactoriesLoader` 从所有 `spring.factories` 文件中加载自动配置类。这些类经过条件注解过滤后，符合条件的才会被注册到 Spring 容器中。</div>
          </div>
          <div class="card-source">来源: 三、SpringBoot 自动配置中的 SPI 应用 -> 1. 自动配置的核心流程 & 2. EnableAutoConfiguration 注解背后的 SPI 机制</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">如何为 SpringBoot 开发一个自定义的自动配置扩展？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">首先，实现一个自定义的自动配置类（使用 @Configuration 和条件注解）；然后在项目的 `src/main/resources/META-INF` 目录下创建 `spring.factories` 文件；最后，在该文件中以 `org.springframework.boot.autoconfigure.EnableAutoConfiguration` 为键，以自定义自动配置类的全限定名为值进行配置。</div>
          </div>
          <div class="card-source">来源: 四、基于 SPI 开发 SpringBoot 扩展 -> 1. 创建自己的 spring.factories 文件 & 2. 实现自定义的自动配置类</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">机制</div>
          <div class="card-question">SpringBoot 是如何将条件注解与 SPI 机制结合起来，以实现更灵活的组件加载的？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">机制</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">SpringBoot 通过 SPI 机制从 `spring.factories` 文件中加载所有潜在的自动配置类。然后，它会逐一检查这些类上的条件注解（如 `@ConditionalOnClass`、`@ConditionalOnProperty` 等）。只有当注解所定义的条件满足时，对应的配置类才会生效，从而实现了按需加载，避免了不必要的组件初始化。</div>
          </div>
          <div class="card-source">来源: 二、SpringBoot SPI 机制的实现原理 -> 4. 条件注解与 SPI 的结合</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">当 SpringBoot 自动配置未按预期生效时，有哪些推荐的排查方法？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">可以从以下几个方面排查：<br>1. 检查 `spring.factories` 文件路径和内容是否正确。<br>2. 检查自动配置类上的条件注解是否满足。<br>3. 检查该自动配置是否被显式排除。<br>4. 在配置文件中设置 `debug=true`，启用并查看自动配置报告，分析生效和未生效的原因。</div>
          </div>
          <div class="card-source">来源: 五、SpringBoot SPI 机制的高级特性 -> 4. 自动配置的失效分析</div>
        </div>
      </div>
    </div>

    <div class="card-container" onclick="this.classList.toggle('flipped');">
      <div class="card">
        <div class="card-face card-front">
          <div class="card-category">技术</div>
          <div class="card-question">在 SpringBoot 中，有哪些方法可以控制自动配置类的加载顺序？</div>
          <div class="card-footer">点击卡片查看答案</div>
        </div>
        <div class="card-face card-back">
          <div class="card-category">技术</div>
          <div class="card-answer-wrapper">
            <div class="card-answer">主要有两种方式：<br>1. 使用注解：`@AutoConfigureOrder`、`@AutoConfigureBefore` 和 `@AutoConfigureAfter` 可以精确控制配置类之间的加载顺序。<br>2. 实现接口：让自动配置类实现 `Ordered` 接口，通过重写 `getOrder()` 方法来指定优先级。</div>
          </div>
          <div class="card-source">来源: 三、SpringBoot 自动配置中的 SPI 应用 -> 3. 自动配置优先级控制 & 五、SpringBoot SPI 机制的高级特性 -> 3. 自动配置的顺序控制</div>
        </div>
      </div>
    </div>

      </div>
    </body>
    </html>
